/**************************************************************************************************
* Title: SumGlobalMemoryManager.cpp
* Author: Gael Huber
* Description: Manager for global memory pools. Holds all global memory pool allocators as well as
*	the global heap allocator and manages requests from global memory.
**************************************************************************************************/
#include "SumGlobalMemoryManager.h"

// Initialize singleton instance of the class
template <> GlobalMemoryManager* Singleton<GlobalMemoryManager>::singleton = 0;

/**************************************************************************************************
* Contructor
**************************************************************************************************/
GlobalMemoryManager::GlobalMemoryManager(void) 
{ }

/**************************************************************************************************
* Destructor
**************************************************************************************************/
GlobalMemoryManager::~GlobalMemoryManager(void)
{ }

/**************************************************************************************************
* Start up global allocator
**************************************************************************************************/
void GlobalMemoryManager::startUp(void) {
	// First determine the number of blocks per pool
	unsigned int numBlocks = 1000;

	// Declare each pool allocator
	level1 = new PoolAllocator(16, numBlocks);
	level2 = new PoolAllocator(32, numBlocks);
	level3 = new PoolAllocator(64, numBlocks);

	// Declare space for the general-purpose heap allocator
	level4 = new HeapAllocator(16 * KILOBYTE);

	// Calculate bounds for all levels
	level1Start = level1->peek();
	level1End = level1Start + 16 * numBlocks;
	level2Start = level2->peek();
	level2End = level2Start + 32 * numBlocks;
	level3Start = level3->peek();
	level3End = level3Start + 64 * numBlocks;
	level4Start = level4->peek();
	level4End = level4Start + 16 * KILOBYTE;

}

/**************************************************************************************************
* Shut down global allocators and free memory
**************************************************************************************************/
void GlobalMemoryManager::shutDown(void) {
	delete level1;
	delete level2;
	delete level3;
	delete level4;
}

/**************************************************************************************************
* Allocate a block of memory
**************************************************************************************************/
void* GlobalMemoryManager::alloc(size_t bytes) {	
	// Address to return
	void* addr = 0;

	// Allocate the memory from whichever level it fits, trickle down to subsequent levels
	if(bytes <= 16) {	// Attempt Level 1
		addr = level1->alloc();

		if(addr == 0) {
			addr = level2->alloc();

			if(addr == 0) {
				addr = level3->alloc();

				if(addr == 0)
					addr = level4->alloc(bytes);
			}
		}
	}
	else if(bytes <= 32) {	// Attempt Level 2
		addr =  level2->alloc();

		if(addr == 0) {
			addr = level3->alloc();

			if(addr == 0)
				addr = level4->alloc(bytes);
		}
	} else if(bytes <= 64) {	// Attempt Level 3
		addr = level3->alloc();

		if(addr == 0)
			addr = level4->alloc(bytes);
	} else {	// Attempt Level 4
		addr = level4->alloc(bytes);
	}

	// Return the result
	return addr;
}

/**************************************************************************************************
* Free a block of memory
**************************************************************************************************/
void GlobalMemoryManager::free(void* mem) {
	// Memory address
	MemAddress address = (MemAddress) mem;
	
	// Find which level this address belongs to and free it
	if(address >= level1Start && address <= level1End)
		level1->free(mem);
	else if(address >= level2Start && address <= level2End)
		level2->free(mem);
	else if(address >= level3Start && address <= level3End)
		level3->free(mem);
	else if(address >= level4Start && address <= level4End)
		level4->free(mem);
}